/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "libzlang_crypto.h"

unsigned char CheckKeyLen( int32_t key_len_type , int32_t key_len )
{
	if( key_len_type == key_len * 8 )
		return TRUE;
	else
		return FALSE;
}

int ZeroPaddingBlockDataUnsafe( char *block_base , int block_data_len , int padding_size )
{
	int	padding_len ;
	
	padding_len = padding_size - (block_data_len%padding_size) ;
	if( padding_len > 0 )
	{
		memset( block_base+block_data_len , 0x00 , padding_len );
	}
	
	return padding_size;
}

int Pkcs7PaddingBlockDataUnsafe( char *block_base , int block_data_len , int padding_size )
{
	int	padding_len ;
	
	padding_len = padding_size - (block_data_len%padding_size) ;
	if( padding_len == 0 && block_data_len > 0 )
		padding_len = padding_size ;
	memset( block_base+block_data_len , (unsigned char)padding_len , padding_len );
	
	return block_data_len+padding_len;
}

int PaddingBlockDataUnsafe( int padding_type , char *block_base , int block_data_len , int padding_size )
{
	if( padding_type == CRYPTO_ZERO_PADDING )
		return ZeroPaddingBlockDataUnsafe( block_base , block_data_len , padding_size );
	else if( padding_type == CRYPTO_PKCS7_PADDING )
		return Pkcs7PaddingBlockDataUnsafe( block_base , block_data_len , padding_size );
	else
		return -1;
}

int ZeroUnpaddingData( char *block_base , int block_data_len )
{
	char	*p_end = block_base + block_data_len-1 ;
	
	while( (*p_end) == '\0' && block_data_len > 0 )
	{
		p_end--;
		block_data_len--;
	}
	
	return block_data_len;
}

int Pkcs7UnpaddingData( char *block_base , int block_data_len )
{
	block_data_len -= block_base[block_data_len-1] ;
	block_base[block_data_len] = '\0' ;
	
	if( block_data_len < 0 )
		return -1;
	else
		return block_data_len;
}

int UnpaddingData( int padding_type , char *block_base , int block_data_len )
{
	if( padding_type == CRYPTO_ZERO_PADDING )
		return ZeroUnpaddingData( block_base , block_data_len );
	else if( padding_type == CRYPTO_PKCS7_PADDING )
		return Pkcs7UnpaddingData( block_base , block_data_len );
	else
		return -1;
}

int EvpCipherEncrypt( struct ZlangRuntime *rt , const EVP_CIPHER *cipher , char *key , char *iv , char *dec , int32_t dec_len , struct ZlangObject *enc_obj )
{
	int32_t		prepare_len ;
	char		**enc = NULL ;
	int32_t		*enc_len = NULL ;
	int		len ;
	
	EVP_CIPHER_CTX	*ctx = NULL ;
	
	int		nret = 0 ;
	
	CallRuntimeFunction_string_Clear( rt , enc_obj );
	if( dec_len == 0 )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "dec_len[%"PRIi32"]" , dec_len )
		return 0;
	}
	prepare_len = dec_len + EVP_CIPHER_block_size(cipher) ; 
	CallRuntimeFunction_string_PrepareBuffer( rt , enc_obj , prepare_len );
	CallRuntimeFunction_string_GetDirectPropertiesPtr( rt , enc_obj , & enc , NULL , & enc_len );
	
	ctx = EVP_CIPHER_CTX_new() ;
	if( ctx == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "EVP_CIPHER_CTX_new failed" )
		UnreferObject( rt , enc_obj );
		return -1;
	}
	
	nret = EVP_EncryptInit_ex( ctx , cipher , NULL , (unsigned char *)key , (unsigned char *)iv ) ;
	if( nret != 1 )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "EVP_EncryptInit_ex failed[%d]" , nret )
		EVP_CIPHER_CTX_free( ctx );
		UnreferObject( rt , enc_obj );
		return -2;
	}
	
	nret = EVP_EncryptUpdate( ctx , (unsigned char *)(*enc) , & len , (unsigned char *)dec , (int)dec_len ) ;
	if( nret != 1 )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "EVP_EncryptUpdate_ex failed[%d]" , nret )
		EVP_CIPHER_CTX_free( ctx );
		UnreferObject( rt , enc_obj );
		return -3;
	}
	(*enc_len) = len ;
	
	nret = EVP_EncryptFinal_ex( ctx , (unsigned char *)(*enc)+(*enc_len) , & len ) ;
	if( nret != 1 )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "EVP_EncryptFinal_ex failed[%d]" , nret )
		EVP_CIPHER_CTX_free( ctx );
		UnreferObject( rt , enc_obj );
		return -4;
	}
	(*enc_len) += len ;
	(*enc)[(*enc_len)] = '\0' ;
	
	EVP_CIPHER_CTX_free( ctx );
	
	return 0;
}

int EvpCipherDecrypt( struct ZlangRuntime *rt , const EVP_CIPHER *cipher , char *key , char *iv , char *enc , int32_t enc_len , struct ZlangObject *dec_obj )
{
	int32_t		prepare_len ;
	char		**dec = NULL ;
	int32_t		*dec_len = NULL ;
	int		len ;
	
	EVP_CIPHER_CTX	*ctx = NULL ;
	
	int		nret = 0 ;
	
	CallRuntimeFunction_string_Clear( rt , dec_obj );
	if( enc_len == 0 )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "enc_len[%"PRIi32"]" , enc_len )
		return 0;
	}
	prepare_len = enc_len + EVP_CIPHER_block_size(cipher) ; 
	CallRuntimeFunction_string_PrepareBuffer( rt , dec_obj , prepare_len );
	CallRuntimeFunction_string_GetDirectPropertiesPtr( rt , dec_obj , & dec , NULL , & dec_len );
	
	ctx = EVP_CIPHER_CTX_new() ;
	if( ctx == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "EVP_CIPHER_CTX_new failed" )
		EVP_CIPHER_CTX_free( ctx );
		UnreferObject( rt , dec_obj );
		return -1;
	}
	
	nret = EVP_DecryptInit_ex( ctx , cipher , NULL , (unsigned char *)key , (unsigned char *)iv ) ;
	if( nret != 1 )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "EVP_DecryptInit_ex failed[%d]" , nret )
		EVP_CIPHER_CTX_free( ctx );
		UnreferObject( rt , dec_obj );
		return -2;
	}
	
	nret = EVP_DecryptUpdate( ctx , (unsigned char *)(*dec) , & len , (unsigned char *)enc , (int)enc_len ) ;
	if( nret != 1 )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "EVP_DecryptUpdate_ex failed[%d]" , nret )
		EVP_CIPHER_CTX_free( ctx );
		UnreferObject( rt , dec_obj );
		return -3;
	}
	(*dec_len) = len ;
	
	nret = EVP_DecryptFinal_ex( ctx , (unsigned char *)(*dec)+(*dec_len) , & len ) ;
	if( nret != 1 )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "EVP_DecryptFinal_ex failed[%d]" , nret )
		EVP_CIPHER_CTX_free( ctx );
		UnreferObject( rt , dec_obj );
		return -4;
	}
	(*dec_len) += len ;
	(*dec)[(*dec_len)] = '\0' ;
	
	EVP_CIPHER_CTX_free( ctx );
	
	return 0;
}

ZlangImportObjectFunction ZlangImportObject_crypto;
ZlangImportObjectFunction ZlangImportObject_des;
ZlangImportObjectFunction ZlangImportObject_tripledes;
ZlangImportObjectFunction ZlangImportObject_aes;
ZlangImportObjectFunction ZlangImportObject_md4;
ZlangImportObjectFunction ZlangImportObject_md5;
ZlangImportObjectFunction ZlangImportObject_sha1;
ZlangImportObjectFunction ZlangImportObject_sha224;
ZlangImportObjectFunction ZlangImportObject_sha256;
ZlangImportObjectFunction ZlangImportObject_sha384;
ZlangImportObjectFunction ZlangImportObject_sha512;
ZlangImportObjectFunction ZlangImportObject_rsakey;
ZlangImportObjectFunction ZlangImportObject_rsa;
#ifndef OPENSSL_NO_SM2
ZlangImportObjectFunction ZlangImportObject_sm2key;
ZlangImportObjectFunction ZlangImportObject_sm2;
#endif
#ifndef OPENSSL_NO_SM3
ZlangImportObjectFunction ZlangImportObject_sm3;
#endif
#ifndef OPENSSL_NO_SM4
ZlangImportObjectFunction ZlangImportObject_sm4;
#endif

#include "charset_GB18030.c"
#include "charset_UTF8.c"

int ZlangImportObjects( struct ZlangRuntime *rt )
{
	struct ZlangObject	*obj = NULL ;
	
	obj = ZlangImportObject_crypto( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_des( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_tripledes( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_aes( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_md4( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_md5( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_sha1( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_sha224( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_sha256( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_sha384( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_sha512( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_rsakey( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_rsa( rt ) ;
	if( obj == NULL )
		return 1;
	
#ifdef SN_sm2
#ifndef OPENSSL_NO_SM2
	obj = ZlangImportObject_sm2key( rt ) ;
	if( obj == NULL )
		return 1;
	
	obj = ZlangImportObject_sm2( rt ) ;
	if( obj == NULL )
		return 1;
#endif
#endif
#ifdef SN_sm3
#ifndef OPENSSL_NO_SM3
	obj = ZlangImportObject_sm3( rt ) ;
	if( obj == NULL )
		return 1;
#endif
#endif
#ifdef SN_sm4_cbc
#ifndef OPENSSL_NO_SM4
	obj = ZlangImportObject_sm4( rt ) ;
	if( obj == NULL )
		return 1;
#endif
#endif

	ImportCharsetAlias( rt , g_zlang_charset_aliases_GB18030 );
	ImportCharsetAlias( rt , g_zlang_charset_aliases_UTF8 );
	
	return 0;
}

